package co.fitstart.weapp.api.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.entity.exception.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dao;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;

import co.fitstart.entity.coach.CoachRestCalendar;
import co.fitstart.entity.coach.CoachRestType;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.site.Site;
import co.fitstart.weapp.api.dao.CoachDao;
import co.fitstart.weapp.api.dao.CourseScheduleDao;
import co.fitstart.weapp.api.service.CoachScheduleService;
import co.fitstart.weapp.api.utils.CourseAppointmentUtils;

/**
 * 
 * @author haiyan_xiao
 *
 */
@Service("coachScheduleService")
public class CoachScheduleServiceImpl implements CoachScheduleService {

    @Dao
    private CoachDao coachDao;

    @Dao
    private CourseScheduleDao courseScheduleDao;

    @Autowired
    private GenericDaoSupport genericDaoSupport;

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoachScheduleService#getTimeBanner(java.util.Date)
     */
    public Map<String, Object> getTimeBanner(Date day) {
        Map<String, Object> map = new HashMap<>();
        
        Date today = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"), "yyyy-MM-dd");
        Date monday = DateUtils.firstWeekDay(day);
        Date sunday = DateUtils.lastWeekDay(day);
        
        map.put("currentDay", day); //当前选择日期
        map.put("hasLastWeek", day.before(org.apache.commons.lang3.time.DateUtils.addWeeks(DateUtils.firstWeekDay(today), -7)) ? false : true); //是否有上一周
        map.put("lastSunday", org.apache.commons.lang3.time.DateUtils.addWeeks(sunday, -1)); //上一周周日
        map.put("today", today); //今日
        map.put("currentWeek", DateUtils.dateBetween(monday, sunday)); //当前选择日期所在周
        map.put("hasNextWeek", day.after(org.apache.commons.lang3.time.DateUtils.addWeeks(DateUtils.lastWeekDay(today), 2)) ? false : true); //是否有下一周
        map.put("nextMonday", org.apache.commons.lang3.time.DateUtils.addWeeks(monday, 1)); //下一周周一
        
        return map;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoachScheduleService#listBusinessTime(co.fitstart.entity.principal.Principal, java.util.Date)
     */
    public List<List<String>> listBusinessTime(Principal coach, Date day) {
        List<List<String>> businessTimes = new ArrayList<>();
        
        Map<String, Date> businessTimePeriod = getBusinessTimePeriod(coach, day);
        if(businessTimePeriod.get("start") != null && businessTimePeriod.get("end") != null) {
            List<String> morning = new ArrayList<>();
            List<String> afternoon = new ArrayList<>();
            List<String> evening = new ArrayList<>();
            
            String dayString = DateFormatUtils.format(day, "yyyy-MM-dd ");
            for(Date appointHour=businessTimePeriod.get("start"); appointHour.before(businessTimePeriod.get("end")); appointHour = org.apache.commons.lang3.time.DateUtils.addMinutes(appointHour, 30)) {
                if(appointHour.before(DateUtils.parseDate(dayString + "14:00:00", "yyyy-MM-dd HH:mm:ss"))) {
                    morning.add(DateFormatUtils.format(appointHour, "HH:mm"));
                } else if(appointHour.before(DateUtils.parseDate(dayString + "18:00:00", "yyyy-MM-dd HH:mm:ss"))) {
                    afternoon.add(DateFormatUtils.format(appointHour, "HH:mm"));
                } else {
                    evening.add(DateFormatUtils.format(appointHour, "HH:mm"));
                }
            }
            
            businessTimes.add(morning);
            businessTimes.add(afternoon);
            businessTimes.add(evening);
        }
        
        return businessTimes;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoachScheduleService#list(co.fitstart.entity.principal.Principal, java.util.Date)
     */
    public Map<String, Object> list(Principal coach, Date day) {
        Map<String, Object> result = new HashMap<>();
        
        List<CoachRestCalendar> coachRestCalendars = coachDao.listCoachRestCalendar(coach, day);
        if(!coachRestCalendars.isEmpty()) {
            for(CoachRestCalendar coachRestCalendar : coachRestCalendars) {
                if((coachRestCalendar.getCoachRestType().isDuty() && result.get(coachRestCalendar.getRestHour()) == null)
                        || (!coachRestCalendar.getCoachRestType().isDuty() && !coachRestCalendar.getCoachRestType().isExperience())) {
                    Map<String, Object> map = new HashMap<>();
                    
                    map.put("type", coachRestCalendar.getCoachRestType().getAlias());
                    map.put("clickable", false);
                    
                    result.put(coachRestCalendar.getRestHour(), map);
                }
            }
        }
        
        List<CourseSchedule> courseSchedules = courseScheduleDao.list(coach, day);
        
        if(!courseSchedules.isEmpty()) {
            for(CourseSchedule courseSchedule : courseSchedules) {
                Map<String, Object> map = new HashMap<>();
                
                if(courseSchedule.getCourseType().isYear()) {
                    map.put("type", "year");
                } else if(courseSchedule.getCourseType().isIndividual()) {
                    if(courseSchedule.getCourse().isExperience()) {
                        map.put("type", "experience");
                    } else if(courseSchedule.getCourse().isGive()) {
                        map.put("type", "give");
                    } else {
                        map.put("type", "individual");
                    }
                }
                map.put("clickable", true);
                map.put("courseRoundId", courseSchedule.getCourseRound().getId());
                map.put("courseScheduleId", courseSchedule.getId());
                
                Date occurHour = DateUtils.parseDate("1900-01-01 " + courseSchedule.getOccurHour() + ":00", "yyyy-MM-dd HH:mm:ss");
                
                result.put(courseSchedule.getOccurHour(), map);
                result.put(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addMinutes(occurHour, 30), "HH:mm"), map);
                if(courseSchedule.getCourse().isExperience()) {
                    result.put(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(occurHour, 1), "HH:mm"), map);
                }
            }
        }
        return result;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoachScheduleService#listBusinessTimes(co.fitstart.entity.principal.Principal, java.util.Date, java.lang.String)
     */
    public List<List<Map<String, Object>>> listBusinessTimes(Principal coach, Date day, String type) {
        List<List<Map<String, Object>>> result = new ArrayList<>();
        List<String> unavailableTimes = new ArrayList<>();
        List<String> restTimes = new ArrayList<>();
        
        if("add".equals(type)) {
            unavailableTimes.addAll(coachDao.listRestTime(coach, day));
            
            List<CourseSchedule> courseSchedules = courseScheduleDao.list(coach, day);
            if(!courseSchedules.isEmpty()) {
                for(CourseSchedule courseSchedule : courseSchedules) {
                    Date occurHour = DateUtils.parseDate("1900-01-01 " + courseSchedule.getOccurHour() + ":00", "yyyy-MM-dd HH:mm:ss");
                    
                    unavailableTimes.add(courseSchedule.getOccurHour());
                    unavailableTimes.add(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addMinutes(occurHour, 30), "HH:mm"));
                    
                    if(courseSchedule.getCourse().isExperience()) {
                        unavailableTimes.add(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(occurHour, 1), "HH:mm"));
                    }
                }
            }
        } else if("delete".equals(type)) {
            restTimes = coachDao.listRestTime(coach, day, CoachRestType.REST);
        }
        
        Map<String, Date> businessTimePeriod = getBusinessTimePeriod(coach, day);
        if(businessTimePeriod.get("start") != null && businessTimePeriod.get("end") != null) {
            List<Map<String, Object>> morning = new ArrayList<>();
            List<Map<String, Object>> afternoon = new ArrayList<>();
            List<Map<String, Object>> evening = new ArrayList<>();
            
            String dayString = DateFormatUtils.format(day, "yyyy-MM-dd ");
            for(Date appointHour=businessTimePeriod.get("start"); appointHour.before(businessTimePeriod.get("end")); appointHour = org.apache.commons.lang3.time.DateUtils.addMinutes(appointHour, 30)) {
                String time = DateFormatUtils.format(appointHour, "HH:mm");
                Map<String, Object> map = new HashMap<>();
                map.put("time", time);
                map.put("selected", false);
                
                if(!appointHour.after(new Date())) {
                    map.put("selectable", false);
                } else if("add".equals(type)) {
                    map.put("selectable", unavailableTimes.contains(time) ? false : true);
                } else if("delete".equals(type)) {
                    map.put("selectable", restTimes.contains(time) ? true : false);
                }
                
                if(appointHour.before(DateUtils.parseDate(dayString + "14:00:00", "yyyy-MM-dd HH:mm:ss"))) {
                    morning.add(map);
                } else if(appointHour.before(DateUtils.parseDate(dayString + "18:00:00", "yyyy-MM-dd HH:mm:ss"))) {
                    afternoon.add(map);
                } else {
                    evening.add(map);
                }
            }
            
            result.add(morning);
            result.add(afternoon);
            result.add(evening);
        }
        
        return result;
    }

    public void rest(Principal coach, String type, Date day, List<String> times) {
        if(times != null && !times.isEmpty()) {
            String dayString = DateFormatUtils.format(day, "yyyy-MM-dd ");
            Date now = new Date();
            List<String> checkScheduleTimes = new ArrayList<>(); //check是否有课，当前时间+半小时前
            List<String> checkExperienceTimes = new ArrayList<>(); //check是否有体验课，一小时前
            
            for(String time : times) {
                Date dateTime = DateUtils.parseDate(dayString + time + ":00", "yyyy-MM-dd HH:mm:ss");
                
                if(dateTime.before(now)) {
                    throw new BusinessException("所选时间小于当前时间，请重新选择");
                } else if("add".equals(type)) {
                    checkScheduleTimes.add(time);
                    checkScheduleTimes.add(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addMinutes(dateTime, -30), "HH:mm"));
                    checkExperienceTimes.add(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(dateTime, -1), "HH:mm"));
                }
            }
            
            if("add".equals(type)) {
                if(coachDao.countCoachRestCalendar(coach, day, times) > 0) {
                    throw new BusinessException("所选时间已有安排，不可添加休息");
                } else if(courseScheduleDao.count(coach, day, checkScheduleTimes) > 0 || courseScheduleDao.countExperience(coach, day, checkScheduleTimes) > 0) {
                    throw new BusinessException("所选时间有课，不可添加休息");
                }
                
                for(String time : times) {
                    genericDaoSupport.save(new CoachRestCalendar(coach, CoachRestType.REST, day, time, false));
                }
            } else if("delete".equals(type)) {
                if(coachDao.countUnrest(coach, day, CoachRestType.REST, times) > 0) {
                    throw new BusinessException("所选时间不可删除休息");
                }
                
                coachDao.disabledRest(coach, day, CoachRestType.REST, times);
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoachScheduleService#reset(co.fitstart.entity.principal.Principal, java.util.Date)
     */
    public void reset(Principal coach, Date day) {
        Date now = new Date();
        
        if(DateUtils.isSameDay(day, now)) {
            coachDao.disabledTodayRest(coach, day, CoachRestType.REST, DateFormatUtils.format(now, "yyyy-MM-dd "), now);
        } else if(day.after(now)) {
            coachDao.disabledRest(coach, day, CoachRestType.REST);
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoachScheduleService#copy(co.fitstart.entity.principal.Principal, java.util.Date)
     */
    public void copy(Principal coach, Date day) {
        Date now = new Date();
        Date today = DateUtils.parseDate(DateFormatUtils.format(now, "yyyy-MM-dd"), "yyyy-MM-dd");
        Date sunday = DateUtils.lastWeekDay(day);
        
        if(day.after(DateUtils.lastWeekDay(today))) {
            Date monday = DateUtils.firstWeekDay(day);
            
            if(coachDao.countCoachRestCalendar(coach, monday, sunday, CoachRestType.REST) > 0) {
                throw new BusinessException("复制必须清空本周休息时间");
            }
            
            List<CoachRestCalendar> lastWeekRests = coachDao.listCoachRestCalendar(coach, org.apache.commons.lang3.time.DateUtils.addWeeks(monday, -1), org.apache.commons.lang3.time.DateUtils.addWeeks(sunday, -1), CoachRestType.REST);
            if(!lastWeekRests.isEmpty()) {
                for(CoachRestCalendar rest : lastWeekRests) {
                    genericDaoSupport.save(new CoachRestCalendar(coach, CoachRestType.REST, org.apache.commons.lang3.time.DateUtils.addWeeks(rest.getRestDate(), 1), rest.getRestHour(), false));
                }
            }
        } else if(!day.before(DateUtils.firstWeekDay(today))) {
            if(coachDao.countCoachRestCalendar(coach, org.apache.commons.lang3.time.DateUtils.addDays(today, 1), sunday, CoachRestType.REST) > 0) {
                throw new BusinessException("复制必须清空本周休息时间");
            } else {
                List<CoachRestCalendar> thisWeekRests = coachDao.listCoachRestCalendar(coach, today, CoachRestType.REST);
                
                if(!thisWeekRests.isEmpty()) {
                    for(CoachRestCalendar rest : thisWeekRests) {
                        if(DateUtils.parseDate(DateFormatUtils.format(rest.getRestDate(), "yyyy-MM-dd ") + rest.getRestHour() + ":00", "yyyy-MM-dd HH:mm:ss").after(now)) {
                            throw new BusinessException("复制必须清空本周休息时间");
                        }
                    }
                }
            }
            
            List<CoachRestCalendar> lastWeekRests = coachDao.listCoachRestCalendar(coach, org.apache.commons.lang3.time.DateUtils.addWeeks(today, -1), org.apache.commons.lang3.time.DateUtils.addWeeks(sunday, -1), CoachRestType.REST);
            if(!lastWeekRests.isEmpty()) {
                for(CoachRestCalendar rest : lastWeekRests) {
                    if(DateUtils.parseDate(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addWeeks(rest.getRestDate(), 1), "yyyy-MM-dd ") + rest.getRestHour() + ":00", "yyyy-MM-dd HH:mm:ss").after(now)) {
                        genericDaoSupport.save(new CoachRestCalendar(coach, CoachRestType.REST, org.apache.commons.lang3.time.DateUtils.addWeeks(rest.getRestDate(), 1), rest.getRestHour(), false));
                    }
                }
            }
        }
    }

    /**
     * 
     * @param coach
     * @param day
     * @return
     */
    private Map<String, Date> getBusinessTimePeriod(Principal coach, Date day) {
        Map<String, Date> map = new HashMap<>();
        
        List<Site> sites = coachDao.listSite(coach, "%" + CourseAppointmentUtils.getDayOfWeek(day) + "%");
        if(!sites.isEmpty()) {
            String dayString = DateFormatUtils.format(day, "yyyy-MM-dd ");
            Date appointStartHour = DateUtils.parseDate(dayString + sites.get(0).getAppointStartHour() + ":00", "yyyy-MM-dd HH:mm:ss");
            Date appointEndHour = DateUtils.parseDate(dayString + sites.get(0).getAppointEndHour() + ":00", "yyyy-MM-dd HH:mm:ss");
            
            for(int i=1; i<sites.size(); i++) {
                Date tempAppointStartHour = DateUtils.parseDate(dayString + sites.get(i).getAppointStartHour() + ":00", "yyyy-MM-dd HH:mm:ss");
                if(tempAppointStartHour.before(appointStartHour)) {
                    appointStartHour = tempAppointStartHour;
                }
                
                Date tempAppointEndHour = DateUtils.parseDate(dayString + sites.get(i).getAppointEndHour() + ":00", "yyyy-MM-dd HH:mm:ss");
                if(tempAppointEndHour.after(appointEndHour)) {
                    appointEndHour = tempAppointEndHour;
                }
            }
            
            map.put("start", appointStartHour);
            map.put("end", appointEndHour);
        }
        
        return map;
    }
}
